package com.pbking.util.logging
{
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	
	import mx.collections.ArrayCollection;
	
	[Event(name="log", type="com.pbking.util.logging.PBLogEvent")]

	/**
	 * The base clase for the logging framework.  Do this instead of trace();<br/>
	 * <br/>
	 * You must first GET an instance of a PBLogger by calling the
	 * public static function "getLogger" and passing in the 
	 * identifier for that logger. (The default is "default").
	 * Using this identifier you can filter your logs, etc.
	 * You should not create an instance of the logger with <code>new PBLogger()</code><br/>
	 * <br/>
	 * With the instance of that logger you can call the various
	 * log levels (debug, info, warn, error, fatal) <br/>
	 * <br/>
	 * example:<br/><code>
	 * var logger:PBLogger = PBLogger.getLogger("com.pbking");
	 * logger.debug("this is my log");
	 * logger.warn("something bad might have just happened");
	 * </code>
	 */
	public class PBLogger extends EventDispatcher
	{
		// VARIABLES //////////
		
		public static var globalTraceLevel:int = 0;

		public var traceLevel:int = 0;

		private static var _loggers:Dictionary = new Dictionary();
		private static var _constructionLocked:Boolean = true;
		private static var _listeners:Dictionary = new Dictionary();
		private var _category:String;		
		
		// CONSTRUCTION //////////
		
		/**
		 * Get/create an instance of a PBLogger object with the
		 * specified category name.  This should be used instead
		 * of <code>new PBLogger();</code>
		 * 
		 * @param category String name of the category to assign 
		 * to the PBLogger object.  This category can be used
		 * to filter logs, etc.  The default is "default".
		 * 
		 * @return PBLogger
		 */
		public static function getLogger(category:String="default"):PBLogger
		{
			var gotLogger:PBLogger = _loggers[category];
			if(!gotLogger)
				gotLogger = createLogger(category);
			return gotLogger;
		}
		
		private static function createLogger(category:String):PBLogger
		{
			//create the logger
			_constructionLocked = false;
			var newLogger:PBLogger = new PBLogger(category);
			_constructionLocked = true;
			
			//store it
			_loggers[category] = newLogger;
			
			//return the logger
			return newLogger;
		}
		
		function PBLogger(category:String)
		{
			if(_constructionLocked)
			{
				throw new Error("PBLogger should be created by calling PBLogger.getLogger('category') NOT by instantiating a new logger");
			}
			
			this._category = category;
		}
		
		// STATIC FUNCTIONS //////////

		/**
		 * Returns an array of ALL of the loggers
		 * that have been created.  This array does
		 * not bind/update if more loggers are created.
		 * 
		 * @return Array
		 */
		public static function allLoggers():Array
		{
			var allLoggers:Array = [];
			for each(var aLogger:PBLogger in _loggers)
				allLoggers.push(aLogger);
				
			return allLoggers;
		}
		
		public static function addEventListener(eventType:String, func:Function):void
		{
			var typeCollection:ArrayCollection = _listeners[eventType];
			
			if(!typeCollection)
			{
				typeCollection = new ArrayCollection();
				_listeners[eventType] = typeCollection;
			}
			
			if(!typeCollection.contains(func))
				typeCollection.addItem(func);
		}
		
		public static function removeEventListener(eventType:String, func:Function):void
		{
			var typeCollection:ArrayCollection = _listeners[eventType];
			
			if(!typeCollection)
				return;
			
			var funcIndex:int = typeCollection.getItemIndex(func);
			
			if(funcIndex == -1)
				return;
				
			typeCollection.removeItemAt(funcIndex);
		}
		
		// PUBLIC FUNCTIONS //////////
		
		/**
		 * Returns the category that was assugned to a PBLogger
		 * when it was created.
		 */
		public function get category():String
		{
			return _category;
		}
		
		/**
		 * Log a message with the specifiec log level.
		 * This is mostly an internal utility used by 
		 * the debug(), warn(), etc methods.  This method
		 * will dispatch the log event and trace the message.
		 * 
		 * @param message String the message to be logged.
		 * 
		 * @param level int the level that the log is.  The 
		 * lower the number, the less severe the log.  Use
		 * <code>PBLogLevel</code> to determine commonly used
		 * log levels or just make up your own if you need 
		 * finer granularity.
		 */
		public function log(message:String, level:int):void
		{
			var e:PBLogEvent = new PBLogEvent(message, level, _category);
			
			if(level >= globalTraceLevel && level >= traceLevel)
				trace(e.toString());
				
			dispatchEvent(e);

			var globalLogListeners:ArrayCollection = _listeners[PBLogEvent.LOG];
			for each(var funct:Function in globalLogListeners)
			{
				funct(e);
			}

		}
		
		/**
		 * dispatches a very low level log event
		 */
		public function debug(message:String):void
		{
			log(message, PBLogLevel.DEBUG);
		}
		
		/**
		 * dispatches a low level log event
		 */
		public function info(message:String):void
		{
			log(message, PBLogLevel.INFO);
		}
		
		/**
		 * dispatches a medium level log event
		 */
		public function warn(message:String):void
		{
			log(message, PBLogLevel.WARN);
		}
		
		/**
		 * dispatches a high level log event
		 */
		public function error(message:String):void
		{
			log(message, PBLogLevel.ERROR);
		}
		
		/**
		 * dispatches a very high level log event
		 */
		public function fatal(message:String):void
		{
			log(message, PBLogLevel.FATAL);
		}
		
	}
}